package com.ruoyi.framework.web.service;

import javax.annotation.Resource;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.WxMaUserService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.ucar.domain.TCustomer;
import com.ruoyi.ucar.util.CommonUtil;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.exception.user.BlackListException;
import com.ruoyi.common.exception.user.CaptchaException;
import com.ruoyi.common.exception.user.CaptchaExpireException;
import com.ruoyi.common.exception.user.UserNotExistsException;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.framework.security.context.AuthenticationContextHolder;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.ucar.mapper.TCustomerMapper;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;


/**
 * 登录校验方法
 * 
 * @author sungangbin
 */
@Component
public class SysLoginService
{
    @Autowired
    private TokenService tokenService;

    @Resource
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedisCache redisCache;
    
    @Autowired
    private ISysUserService userService;

    @Autowired
    private ISysConfigService configService;

    @Autowired
    private TCustomerMapper tCustomerMapper;

    @Autowired
    private WxMaService wxMaService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    // 应用ID和应用秘钥，需要在微信公众平台中申请
    @Value("${ucar.wx.miniapp.appid}")
    private   String appId  ;
    @Value("${ucar.wx.miniapp.secret}")
    private   String appSecret;

    // 获取微信用户access_token的接口地址
    private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token";
    // 获取微信用户信息的接口地址
    private static final String USER_INFO_URL = "https://api.weixin.qq.com/sns/userinfo";


    private   Map<String, String> getAccessToken(String code) throws Exception {
        OkHttpClient client = new OkHttpClient();
        String url = ACCESS_TOKEN_URL + "?appid=" + appId + "&secret=" + appSecret + "&code=" + code
                + "&grant_type=authorization_code";
        Request request = new Request.Builder().url(url).get().build();
        Response response = client.newCall(request).execute();
        String responseStr = response.body().string();
        JSONObject jsonObject = JSONUtil.parseObj (responseStr);
        String accessToken = jsonObject.getStr("access_token");
        String openid = jsonObject.getStr("openid");
        Map<String, String> map = new HashMap<>();
        map.put("access_token", accessToken);
        map.put("openid", openid);
        return map;
    }
    /**
     * 获取微信用户信息
     *
     * @param accessToken 用户的access_token
     * @param openid 用户的openid
     * @return 包含用户信息的Map
     * @throws Exception
     */
    private   Map<String, String> getUserInfo(String accessToken, String openid) throws Exception {
        OkHttpClient client = new OkHttpClient();
        String url = USER_INFO_URL + "?access_token=" + accessToken + "&openid=" + openid + "&lang=zh_CN";
        Request request = new Request.Builder().url(url).get().build();
        Response response = client.newCall(request).execute();
        String responseStr = response.body().string();
        JSONObject jsonObject = JSONUtil.parseObj (responseStr);
        String nickname = jsonObject.getStr("nickname");
        String avatar = jsonObject.getStr("headimgurl");
        String phoneNumber = jsonObject.getStr("phoneNumber"); // 获取用户手机号需要用户在小程序内进行授权
        Map<String, String> map = new HashMap<>();
        map.put("nickname", nickname);
        map.put("avatar", avatar);
        map.put("phoneNumber", phoneNumber);
        return map;
    }



    /**
     * 登录验证
     * 
     * @param username 用户名
     * @param password 密码
     * @param code 验证码
     * @param uuid 唯一标识
     * @return 结果
     */
    public String login(String username, String password, String code, String uuid,String loginSource)
    {
        // 验证码校验
        validateCaptcha(username, code, uuid);
        // 登录前置校验
        loginPreCheck(username, password);
        // 用户验证
        Authentication authentication = null;
        try
        {
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
            AuthenticationContextHolder.setContext(authenticationToken);
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(authenticationToken);
        }
        catch (Exception e)
        {
            if (e instanceof BadCredentialsException)
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                throw new UserPasswordNotMatchException();
            }
            else
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
                throw new ServiceException(e.getMessage());
            }
        }
        finally
        {
            AuthenticationContextHolder.clearContext();
        }
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        loginUser.setLoginSource(loginSource);
        recordLoginInfo(loginUser.getUserId());
        // 生成token
        return tokenService.createToken(loginUser);
    }


    public String decryptPhoneNumber(String encryptedData, String sessionKey, String iv) throws Exception {
        return WxMaCryptUtils.decrypt(sessionKey, encryptedData, iv);
    }


    public Map<String,Object> mobileLogin(String encryptedData, String  iv, String code,String  parentCustomerUuid,Long customerId ) throws Exception {
        String phoneNumber ="一键登录失败";
        try
        {
//            Map<String,String> map = getAccessToken(code);
//            logger.info("微信一键登录返回session对象：{}",map );
//            map =getUserInfo(map.get("access_token"),map.get("openid"));
//            logger.info("微信一键登录返回session对象：{}",map );
            WxMaUserService userService2 = wxMaService.getUserService();
            // 1. 获取session_key和openid
            WxMaJscode2SessionResult session = userService2
                    .getSessionInfo(code);
            logger.info("微信一键登录返回session对象：{}",session.toString() );
            String openId=session.getOpenid();
//            String sessionKey=redisTemplate.opsForValue().get("wx:session:" + openId);
//            logger.info("redis缓存中返回得sessionKey--111：{}",sessionKey );
//
//            if(StringUtils.isNotEmpty(sessionKey)){
//                String phoneJson=decryptPhoneNumber(encryptedData,sessionKey, iv);
//                logger.info("解析手机号的JSON数据：{}",phoneJson );
//                JSONObject phoneObj = JSON.parseObject(phoneJson);
//                phoneNumber = phoneObj.getString("purePhoneNumber");
//            }else {
                String sessionKey=session.getSessionKey();
//                String phoneJson=decryptPhoneNumber(encryptedData,sessionKey, iv);
//                logger.info("解析手机号的JSON数据：{}",phoneJson );
//                JSONObject phoneObj=  JSONUtil.parseObj(phoneJson);
//                phoneNumber = phoneObj.get("phoneNumber").toString();
            WxMaPhoneNumberInfo phoneNoInfo = userService2.getPhoneNoInfo(sessionKey, encryptedData, iv);
            phoneNumber = phoneNoInfo.getPhoneNumber();
//            WxMaUserInfo  wxMaUserInfo=userService2.getUserInfo(sessionKey, encryptedData, iv);

                // 缓存session_key（有效期2小时）
//                redisTemplate.opsForValue().set(
//                        "wx:session:" + openId,
//                        session.getSessionKey(),
//                        2, TimeUnit.HOURS
//                );
//            }
            logger.info("微信一键登录返回的手机号码数据：{}",phoneNumber );
//            logger.info("微信一键登录返回的用户数据：{}",wxMaUserInfo );
            // 用户验证
            Authentication authentication = null;
            SysUser sysUser=userService.selectUserByUserName(phoneNumber);
            if(StringUtils.isNull(sysUser)){
                sysUser= new SysUser();
                sysUser.setUserName(phoneNumber);
                sysUser.setNickName(phoneNumber);
                sysUser.setPhonenumber(phoneNumber);
                userService.saveSysUser(sysUser);
            }
            TCustomer tCustomer=  tCustomerMapper.selectOne(new LambdaQueryWrapper<TCustomer>().eq(TCustomer::getCustomerPhone,phoneNumber));
            if(StringUtils.isNull(tCustomer)){
                //客户表保存数据
                tCustomer=new TCustomer();
                tCustomer.setCustomerName(phoneNumber);
                tCustomer.setCustomerPhone(phoneNumber);
                tCustomer.setCustomerWechat(phoneNumber);
                tCustomer.setCustomerUuid(CommonUtil.createUuid());
                tCustomer.setCustomerType("commonCus");//commonCus普通客户
                tCustomer.setBalanceNumber(new BigDecimal(0.00));
                tCustomer.setRewardFlag(0l);
                tCustomer.setSysUserId(sysUser.getUserId());
                tCustomer.setDeleteFlag(1L);
                tCustomer.setCreatedBy("admin");
                if(StringUtils.isNotEmpty(parentCustomerUuid)&&!"null".equals(parentCustomerUuid)){
                    TCustomer tCustomer1=tCustomerMapper.selectOne(new LambdaQueryWrapper<TCustomer>().eq(TCustomer::getDeleteFlag,1l).eq(TCustomer::getCustomerUuid,parentCustomerUuid));
                    if(StringUtils.isNotNull(tCustomer1)&&"first_agent".equals(tCustomer1.getAgentGrade())&&"agentCus".equals(tCustomer1.getCustomerType())){
                        tCustomer.setCustomerType("agentCus");//代理客户
                        tCustomer.setAgentGrade("second_agent");
                    }
                    if(StringUtils.isNotNull(tCustomer1)&&"agentCus".equals(tCustomer1.getCustomerType())){
                       tCustomer.setParentCustomerUuid(parentCustomerUuid);
                    }
                }else{
                    if(StringUtils.isNotNull(customerId)){
                        TCustomer tCustomer1=tCustomerMapper.selectOne(new LambdaQueryWrapper<TCustomer>().eq(TCustomer::getDeleteFlag,1l).eq(TCustomer::getId,customerId));
                        if(StringUtils.isNotNull(tCustomer1)&&"first_agent".equals(tCustomer1.getAgentGrade())&&"agentCus".equals(tCustomer1.getCustomerType())){
                            tCustomer.setCustomerType("agentCus");//代理客户
                            tCustomer.setAgentGrade("second_agent");
                        }
                        if(StringUtils.isNotNull(tCustomer1)&&"agentCus".equals(tCustomer1.getCustomerType())) {
                            tCustomer.setParentCustomerUuid(tCustomer1.getCustomerUuid());
                        }
                    }
                }
                tCustomerMapper.insert(tCustomer);
            }else{
                if(!tCustomer.getDeleteFlag().equals(1l)){
                    tCustomer.setDeleteFlag(1l);
                    tCustomerMapper.updateById(tCustomer);
                }
            }
            //默认密码
            String password = configService.selectConfigByKey("sys.user.initPassword");
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(phoneNumber, password);
            AuthenticationContextHolder.setContext(authenticationToken);
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(authenticationToken);
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(phoneNumber, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
            LoginUser loginUser = (LoginUser) authentication.getPrincipal();
            loginUser.setLoginSource("mobile");
            recordLoginInfo(loginUser.getUserId());
            Map<String,Object> hashMap=new HashMap<String,Object>();
            hashMap.put("token",tokenService.createToken(loginUser));
            hashMap.put("openid",openId);
            // 生成token
            return hashMap;
        }
        catch (Exception e)
        {
            if (e instanceof BadCredentialsException)
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(phoneNumber, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                throw new UserPasswordNotMatchException();
            }
            else
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(phoneNumber, Constants.LOGIN_FAIL, e.getMessage()));
                throw new ServiceException(e.getMessage());
            }
        }
        finally
        {
            AuthenticationContextHolder.clearContext();
        }

    }

    public Map<String,Object> mobileSmsLogin(String mobilePhoneNumber, String  smsCode,String code,String  parentCustomerUuid,Long customerId ) throws Exception {

        Map<String,Object> hashMap=new HashMap<String,Object>();
        if(!CommonUtil.isMobilePhoneValid(mobilePhoneNumber)){
            hashMap.put("msg","手机号码格式错误！");
            return hashMap;
        }
        TCustomer tCustomer=  tCustomerMapper.selectOne(new LambdaQueryWrapper<TCustomer>().eq(TCustomer::getCustomerPhone,mobilePhoneNumber));
        if(StringUtils.isNull(tCustomer)&&StringUtils.isEmpty(code)){
                hashMap.put("msg","首次登录，微信小程序Code必须传递！");
                return hashMap;
        }
        String redisSmsCode=redisTemplate.opsForValue().get("sms:" + mobilePhoneNumber);
        if(StringUtils.isNotEmpty(redisSmsCode)){
         if(smsCode.equals(redisSmsCode)){
             // 用户验证
             Authentication authentication = null;
             try
             {
                 SysUser sysUser=userService.selectUserByUserName(mobilePhoneNumber);
                 if(StringUtils.isNull(sysUser)){
                       sysUser= new SysUser();
                       sysUser.setUserName(mobilePhoneNumber);
                       sysUser.setNickName(mobilePhoneNumber);
                       sysUser.setPhonenumber(mobilePhoneNumber);
                       userService.saveSysUser(sysUser);
                 }
                  if(StringUtils.isNull(tCustomer)){

//                     Map<String,String> map = getAccessToken(code);
//                     map =getUserInfo(map.get("access_token"),map.get("openid"));

                     //客户表保存数据
                     tCustomer=new TCustomer();
                     tCustomer.setCustomerName(mobilePhoneNumber);
                     tCustomer.setCustomerPhone(mobilePhoneNumber);
                     tCustomer.setCustomerWechat(mobilePhoneNumber);
                     tCustomer.setCustomerUuid(CommonUtil.createUuid());
                     tCustomer.setCustomerType("commonCus");//commonCus普通客户
                     tCustomer.setBalanceNumber(new BigDecimal(0.00));
                     tCustomer.setRewardFlag(0l);
                     tCustomer.setSysUserId(sysUser.getUserId());
                     tCustomer.setDeleteFlag(1L);
                     tCustomer.setCreatedBy("admin");
                     if(StringUtils.isNotEmpty(parentCustomerUuid)&&!"null".equals(parentCustomerUuid)){
                         TCustomer tCustomer1=tCustomerMapper.selectOne(new LambdaQueryWrapper<TCustomer>().eq(TCustomer::getDeleteFlag,1l).eq(TCustomer::getCustomerUuid,parentCustomerUuid));
                         if(StringUtils.isNotNull(tCustomer1)&&"first_agent".equals(tCustomer1.getAgentGrade())&&"agentCus".equals(tCustomer1.getCustomerType())){
                             tCustomer.setCustomerType("agentCus");//代理客户
                             tCustomer.setAgentGrade("second_agent");
                         }
                         if(StringUtils.isNotNull(tCustomer1)&&"agentCus".equals(tCustomer1.getCustomerType())){
                            tCustomer.setParentCustomerUuid(parentCustomerUuid);
                         }
                     }else{
                         if(StringUtils.isNotNull(customerId)){
                             TCustomer tCustomer1=tCustomerMapper.selectOne(new LambdaQueryWrapper<TCustomer>().eq(TCustomer::getDeleteFlag,1l).eq(TCustomer::getId,customerId));
                             if(StringUtils.isNotNull(tCustomer1)&&"first_agent".equals(tCustomer1.getAgentGrade())&&"agentCus".equals(tCustomer1.getCustomerType())){
                                 tCustomer.setCustomerType("agentCus");//代理客户
                                 tCustomer.setAgentGrade("second_agent");
                             }
                             if(StringUtils.isNotNull(tCustomer1)&&"agentCus".equals(tCustomer1.getCustomerType())){
                                 tCustomer.setParentCustomerUuid(tCustomer1.getCustomerUuid());
                             }
                         }
                     }
                     tCustomerMapper.insert(tCustomer);
                 }else{
                     if(!tCustomer.getDeleteFlag().equals(1l)){
                         tCustomer.setDeleteFlag(1l);
                         tCustomerMapper.updateById(tCustomer);
                     }
                 }
                 //默认密码
                 String password = configService.selectConfigByKey("sys.user.initPassword");
                 UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(mobilePhoneNumber, password);
                 AuthenticationContextHolder.setContext(authenticationToken);
                 // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
                 authentication = authenticationManager.authenticate(authenticationToken);
             }
             catch (Exception e)
             {
                 if (e instanceof BadCredentialsException)
                 {
                     AsyncManager.me().execute(AsyncFactory.recordLogininfor(mobilePhoneNumber, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                     throw new UserPasswordNotMatchException();
                 }
                 else
                 {
                     AsyncManager.me().execute(AsyncFactory.recordLogininfor(mobilePhoneNumber, Constants.LOGIN_FAIL, e.getMessage()));
                     throw new ServiceException(e.getMessage());
                 }
             }
             finally
             {
                 AuthenticationContextHolder.clearContext();
             }
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(mobilePhoneNumber, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
             LoginUser loginUser = (LoginUser) authentication.getPrincipal();
             loginUser.setLoginSource("mobile");
             recordLoginInfo(loginUser.getUserId());

             hashMap.put("token",tokenService.createToken(loginUser));
             return hashMap;
         }else{
             hashMap.put("msg","您输入得验证码错误！");
             return hashMap;
         }
        }else {
            hashMap.put("msg","验证码已过期，请重新获取验证码！");
            return hashMap;
        }
    }

    /**
     * 校验验证码
     * 
     * @param username 用户名
     * @param code 验证码
     * @param uuid 唯一标识
     * @return 结果
     */
    public void validateCaptcha(String username, String code, String uuid)
    {
        boolean captchaEnabled = configService.selectCaptchaEnabled();
        if (captchaEnabled)
        {
            String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
            String captcha = redisCache.getCacheObject(verifyKey);
            if (captcha == null)
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
                throw new CaptchaExpireException();
            }
            redisCache.deleteObject(verifyKey);
            if (!code.equalsIgnoreCase(captcha))
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
                throw new CaptchaException();
            }
        }
    }

    /**
     * 登录前置校验
     * @param username 用户名
     * @param password 用户密码
     */
    public void loginPreCheck(String username, String password)
    {
        // 用户名或密码为空 错误
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password))
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
            throw new UserNotExistsException();
        }
        // 密码如果不在指定范围内 错误
        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
            throw new UserPasswordNotMatchException();
        }
        // 用户名不在指定范围内 错误
        if (username.length() < UserConstants.USERNAME_MIN_LENGTH
                || username.length() > UserConstants.USERNAME_MAX_LENGTH)
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
            throw new UserPasswordNotMatchException();
        }
        // IP黑名单校验
        String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));
            throw new BlackListException();
        }
    }

    /**
     * 记录登录信息
     *
     * @param userId 用户ID
     */
    public void recordLoginInfo(Long userId)
    {
        SysUser sysUser = new SysUser();
        sysUser.setUserId(userId);
        sysUser.setLoginIp(IpUtils.getIpAddr());
        sysUser.setLoginDate(DateUtils.getNowDate());
        userService.updateUserProfile(sysUser);
    }
}
